package cz.muni.fi.pv112.cranesimulator.scene.handlers;

import cz.muni.fi.pv112.cranesimulator.scene.Scene;
import cz.muni.fi.pv112.cranesimulator.scene.items.CameraInterface;
import cz.muni.fi.pv112.cranesimulator.settings.Controls;
import cz.muni.fi.pv112.cranesimulator.utils.handlers.inputs.KeyInputHandler;
import javax.media.opengl.glu.GLU;
import static cz.muni.fi.pv112.cranesimulator.utils.Calculator.moveBy;
import static cz.muni.fi.pv112.cranesimulator.utils.Calculator.recalculateCoordinatesToSceneCoordinateSystem;
import static java.lang.Math.abs;

/**
 *
 * @author Michal Ďuriš
 */
public class CameraHandler {

    private CameraInterface camera;

    private KeyInputHandler keyInputHandler;

    private final Controls controls = Controls.getInstance();

    private final float[] sceneBorders = {Float.MAX_VALUE,
                                          Float.MAX_VALUE,
                                          Float.MAX_VALUE};

    public CameraHandler() {
    }

    public void setKeyInputHandler(KeyInputHandler keyInputHandler) {
        this.keyInputHandler = keyInputHandler;
    }

    public void setCamera(CameraInterface camera) {
        this.camera = camera;
    }

    public CameraInterface getCamera() {
        return camera;
    }

    public void setSceneBorders(float x, float y, float z) {
        sceneBorders[Scene.X_AXIS] = x - 1.0f;
        sceneBorders[Scene.Y_AXIS] = y - 1.0f;
        sceneBorders[Scene.Z_AXIS] = z - 1.0f;
    }

    public void moveCameraBy(float x, float y, float z) {
        translate(x, y, z);
    }

    private void translate(float moveX, float moveY, float moveZ) {

        if (camera.isMoveEnabled()) {

            float[] newCameraPosition = moveBy(moveX, moveY, moveZ, camera
                    .getPosition(), camera
                    .getOrientation());

            if (abs(newCameraPosition[Scene.X_AXIS])
                    < sceneBorders[Scene.X_AXIS]
                    && newCameraPosition[Scene.Y_AXIS] > 0.0f
                    && newCameraPosition[Scene.Y_AXIS]
                    < sceneBorders[Scene.Y_AXIS]
                    && abs(newCameraPosition[Scene.Z_AXIS])
                    < sceneBorders[Scene.Z_AXIS]) {

                camera.setPosition(newCameraPosition);
            }
        }
    }

    public void rotateCamera(float[] angles) {
        rotate(angles[Scene.X_AXIS], angles[Scene.Y_AXIS]);
    }

    private void rotate(float angleX, float angleY) {

        if (camera.isRotationEnabled()) {
            float newAngleX = (camera.getOrientationAngleX() + angleX) % 360.0f;
            float newAngleY = (camera.getOrientationAngleY() + angleY) % 360.0f;

            camera.setOrientation(newAngleX, newAngleY, camera
                    .getOrientationAngleZ());
        }
    }

    public void lookAt(GLU glu) {

        float[] upVector = recalculateCoordinatesToSceneCoordinateSystem(0.0f,
                1.0f,
                0.0f,
                camera
                .getOrientation());
        float[] forwardVector = recalculateCoordinatesToSceneCoordinateSystem(
                0.0f, 0.0f, 1.0f, camera.getOrientation());

        glu.gluLookAt(camera.getPositionX(),
                camera.getPositionY(),
                camera.getPositionZ(),
                camera.getPositionX() + forwardVector[Scene.X_AXIS],
                camera.getPositionY() + forwardVector[Scene.Y_AXIS],
                camera.getPositionZ() + forwardVector[Scene.Z_AXIS],
                upVector[Scene.X_AXIS],
                upVector[Scene.Y_AXIS],
                upVector[Scene.Z_AXIS]);
    }

}
